using Fusion.Async.Timers;
using Fusion.Net.TCP;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Sockets;

namespace Fusion.Net
{
    public class NetDataHandler
    {
        Timer dataHandler;
        NetBase netBase;
        TCPServer tcpServer;
        public NetDataHandler(NetBase netBase)
        {
            this.netBase = netBase;
            dataHandler = new Timer().TypeSetter(Timer.InvokeType.Frame).PersistentSetter(true).TagSetter("NetDataHandler").Invoke(HandleDatas);
            if (netBase is TCPServer)
            {
                tcpServer = (TCPServer)netBase;
            }
        }

        public void Close()
        {
            dataHandler?.Stop();
            dataHandler = null;
        }

        public enum DataType
        { 
            Data = 0,
            HeartBeats = 1,
            Connected = 2,
            Error = 3,
        }


        public struct DataWrap
        {
            private byte[] data;

            private DataType dataType;
            private ExceptionData exception;

            public DataWrap(byte[] data, DataType dataType, ExceptionData exception)
            {
                this.data = data;
                this.dataType = dataType;

                this.exception = exception;
            }

            public DataWrap(byte[] data, DataType dataType) : this(data, dataType, default)
            {
            }

            public DataWrap(byte[] data) : this(data, 0)
            {
            }
            public byte[] Data { get => data; }
            public DataType DataType { get => dataType; }
            public ExceptionData Exception { get => exception; }
        }

        protected Queue<DataWrap> dataQueue = new Queue<DataWrap>();
        protected Queue<SocketBase> socketQueue = new Queue<SocketBase>();

        public void Enqueue(SocketBase socket, DataWrap data)
        {
            lock (this)
            {
                socketQueue.Enqueue(socket);
                dataQueue.Enqueue(data);
            }
        }

        public void HandleDatas()
        {
            if (dataQueue.Count == 0)
            {
                return;
            }

            lock (this)
            {
                while (dataQueue.TryDequeue(out DataWrap data))
                {
                    SocketBase socketBase = socketQueue.Dequeue();
                    HandleData(socketBase, data);
                }
            }
        }

        protected void HandleData(SocketBase socket, DataWrap data)
        {
            if (data.DataType == DataType.Data)
            {
                netBase.InvokeOnMessage(socket, data.Data);
            }
            else if (data.DataType == DataType.HeartBeats)
            {
                netBase.InvokeOnHeartBeated(socket);
            }
            else if (data.DataType == DataType.Connected)
            {
                tcpServer?.InvokeOnConnected(socket);
            }
            else if (data.DataType == DataType.Error)
            {
                netBase.InvokeOnError(socket, data.Exception);
            }
        }
    }
}

